package com.cyh.testregularexp;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.util.Log;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity {
    private String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //test1();
        //test2();
        //test3();
        //test4();
        //test5();
        //test6();
        //test7();
        //test8();
        //test9();
        //test10();
        //test11();
        //test12();
        //test13();
        //test14();
        //test15();
        //test16();
        //test17();
        //test18();
        //test19();
        test20();
    }

    /**
     * 提前文章中的英文单词
     * */
    private void test1() {
        String str = "2005年6月，在Java One大会上，Sun公司发布了Java SE 6。此时，Java的各种版本已经更名，已取消其中的数字2，如J2EE更名为JavaEE，J2SE更名为JavaSE，J2ME更名为JavaME";
        //找英文单词
        //Pattern pattern = Pattern.compile("[a-zA-Z]+");
        //找数字
        //Pattern pattern = Pattern.compile("[0-9]+");
        //找数字和英文单词
        Pattern pattern = Pattern.compile("([a-zA-Z]+)|([0-9]+)");
        Matcher matcher = pattern.matcher(str);


        while (matcher.find()) { //判断是否可以找到下一个符合条件的数据
            String content = matcher.group();//找出下一个符合条件的数据
            Log.i(TAG, "找到内容 = " + content);
        }
    }

    /**
     * java正在表达式的原理（重要）
     * match.find() 完成的任务：
     * 1.根据指定的规则，定位满足规则的子字符串（比如1234）
     * 2.找到后，将子字符串的开始索引记录到matcher对象的属性 int[] groups;
     *   groups[0] = 2, 把该子字符串的结束索引+1的值记录到groups[1] = 6.
     * 3.同时记录oldLast的值为子字符串的结束的索引+1的值即6， 即下次执行find时，就从6开始匹配。
     *
     * matcher.group(0)分析：
     * 1.根据groups[0]=2 和 groups[1]=6的记录的位置，从str开始截取子字符串返回。就是[2, 6)
     * 2.如果再次执行matcher.find()方法，groups数组会清空，重新设置下一个，groups[0]=8,groups[1]=12;
     * */
    private void test2() {
        String str = "aa1234bb5678d9d";
        String regStr = "\\d\\d\\d\\d";//找出4位的数字
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(str); //创建匹配器
        int count = matcher.groupCount();
        Log.i(TAG, "多少个分组=" + count);//0
        //开始匹配
        while (matcher.find()) {
            String content = matcher.group(0);
            Log.i(TAG, "找到内容 = " + content);
        }
    }

    /**
     * 在test2()的基础上分组
     * 什么是分组 表达式值由小括号的表示分组，第一个小括号表示第一组，第二个小括号表示第二组...
     * 找到1345， 总的字符串的索引：groups[0]=2,groups[1]=6,
     * 记录第一组小括号匹配的索引：groups[2]=2, groups[3]=4;
     * 记录第二组小括号匹配的索引：groups[4]=4, groups[5]=6;
     *
     * 第二次find时，groups清空，重新开始。
     * */
    private void test3() {
        String str = "aa1234bb5678d9d";
        String regStr = "(\\d\\d)(\\d\\d)";
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(str); //创建匹配器
        int count = matcher.groupCount();
        Log.i(TAG, "多少个分组=" + count);//2
        //开始匹配
        while (matcher.find()) {
            String content = matcher.group(); //1234 或 5678
            String content_0 = matcher.group(0);//1234 或 5678
            String content_1 = matcher.group(1);//12 或 56
            String content_2 = matcher.group(2);//34 或 78
            //String content_3 = matcher.group(3); 报错
            Log.i(TAG, "找到内容content = " + content);
            Log.i(TAG, "找到内容content_0 = " + content_0);
            Log.i(TAG, "找到内容content_1 = " + content_1);
            Log.i(TAG, "找到内容content_2 = " + content_2);
            //Log.i(TAG, "找到内容content_3 = " + content_3);
        }
    }

    /**
     * 正则表达式语法：元字符包括6大类：
     * 1.限定符
     * 2.选择匹配符
     * 3.分组组合和反向引用符
     * 4.特殊字符
     * 5.字符匹配符
     * 6.定位符
     * */

    /**
     * 转义字符的使用//
     * */
    private void test4() {
        String content = "abc$(123(456(def(";
        //匹配(
        String regStr = "\\(";
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * [] -> 可接收的字符列表 -> [abc] -> abcd中的任意一个字符
     * [^] -> 不接收的字符列表 -> [^abc] -> 除abcd之外的任意一个字符，包括数字和特殊字符
     * - -> 连字符 -> [a-d] -> abcd中的任意一个字符。
     * . -> 匹配除\n以外的任何字符 -> a..b 》 表示以a开头，b结尾，中间两个字符，长度为4的字符串  -> aaab、a3fb、a#*b
     * \\d ->单个数字字符，相当于[0-9] -> \\d{3}(\\d)? -> 包含3个或者4个数字的字符串 -> 123、 9876
     * \\D -> 非单个数字的字符，相当于[^0-9] -> \\D(\\d)* -> 以单个非数字开头，后接任意个数字符串 -> a、 a123
     * \\w -> 匹配单个数字、大小写字母字符和下划线，相当于[0-9a-zA-Z_] -> \\d{3}\\w{4} -> 以3个数字字符开头的长度为7的数字字母字符串 -> 123abcd、12345ab
     * \\W -> 匹配非单个数字、大小写字母字符和下划线，相当于[^0-9a-zA-Z_] -> \\W+\\d{2} -> 以至少1个非数字非字母开头，2个数字字符结尾 -> #29,#?@10
     * \\s -> 匹配任何空白字符（空格、制表符）
     * \\S -> 匹配任何非空白字符
     * */

    /**
     * 不区分大小写
     * */
    private void test5() {
        String content = "12ABC34";
        //匹配(
        String regStr = "(?i)abc"; //(?i)表示不区分大小写， abc不区分大小写
        //String regStr = "a(?i)bc"; //bc不区分大小写
        //String regStr = "a((?i)b)c"; //b不区分大小写
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        //Pattern.compile(regStr, Pattern.CASE_INSENSITIVE);//第二种方法表示不区分大小写
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * 选择匹配a|b,表示匹配a或者b
     * */
    private void test6() {
        String content = "hanshunping  韩 寒冷";
        String regStr = "han|寒|韩";
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * 元字符-限定符
     *   * -> 指定重复0次或n次 -> (abc)* -> abc、 abcabc
     *   + -> 指定重复1次或n次（至少一次） -> m+(abc)* -> m、mabc、mmmab、mmmabc
     *   ? -> 指定重复0次或1次（最多一次） -> m+abc? -> mab、mabc、mmmab、mmabc
     *   {n} -> 指定重复n次 -> [abcd]{3} -> abc、dbc、adc
     *   {n,} -> 指定重复至少n次 -> [abcd]{3,} -> aab、dbc、aaabdc
     *   {n,m} -> 指定重复至少n次，最多m次 -> [abcd]{3,5} -> abc、abcd、aaaaa、 bcdab
     *
     *   细节：java匹配默认贪婪匹配，即尽可能匹配多的。
     * */
    private void test7() {
        String content = "11abcabc22abc33";
        //String regStr = "(abc)+"; //2个结果
        //String regStr = "e*";//16个结果
        String regStr = "(eeeeeeeeeeeee)*";// 16个结果
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * 元字符-定位符
     *  ^ -> 指定起始字符 -> ^[0-9]+[a-z]* -> 123、6aa、55efd
     *  $ -> 指定结束字符 -> ^[0-9]\\-[a-z]+$ -> 1-a
     *  \\b -> 匹配目标字符串的边界 -> han\\b -> 这里说的字符串的边界是指子串间有空格，或者是目标字符串的结束位置
     *  \\B -> 匹配目标字符串的非边界 -> han\\B
     * */
    private void test8() {
        String content = "hanshunping sphan nnhan";
        //String regStr = "han\\b"; //匹配两个,最后两个han,因为它后面有空格或者结束
        String regStr = "han\\B"; //匹配1个， 匹配第一个han
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * 分组：常用分组
     * (pattern) 非命名组 通过编号1、2、3来获取组
     * (?<name>pattern) 命名组， 既可以通过编号也可以通过名字来获取组
     * */
    @RequiresApi(api = Build.VERSION_CODES.O) //命名分组有api版本的要求
    private void test9() {
        String content = "hanshunping s7789 nn1123han";
        //String regStr = "(\\d\\d)(\\d\\d)"; //非命名分组
        String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)"; //命名分组
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();
            String str_0 = matcher.group(0);
            String str_1 = matcher.group(1);
            String str_2 = matcher.group(2);
            String str_g1 = matcher.group("g1");
            String str_g2 = matcher.group("g2");

            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str_0 = " + str_0);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str_1 = " + str_1);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str_2 = " + str_2);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str_g1 = " + str_g1);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str_g2 = " + str_g2);
            index++;
        }
    }


    /**
     * 分组：特别分组
     * (?:pattern) 没有编号1、2、3。匹配pattern但不捕获该匹配的子表达式，即他是一个非捕获匹配，不存储供以后的匹配。
     *             这对于用"or"字符(|)组合模式部件的情况很有用。
     *             例如：industr(?:y|ies) 是比 industry|industries 更经济的表达式
     * (?=pattern) 他是一个非捕获匹配。例如： Windows(?=95|98|NT|2000)匹配Windows2000中的Windows, 但
     *             不匹配Windows3.1中的Windows
     * (?!pattern) 该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。他是一个非捕获匹配。
     *             例如：Windows(?!95|98|NT|2000)匹配Windows3.1中的Windows, 但
     *             不匹配Windows2000中的Windows
     * */
    private void test10() {
        String content = "hello韩顺平教育 jack韩顺平老师 韩顺平同学hello";

        //找到 韩顺平教育、韩顺平老师、韩顺平同学 子字符串
        //String regStr = "韩顺平教育|韩顺平老师|韩顺平同学";
        //String regStr = "韩顺平(?:教育|老师|同学)"; //使用matcher.group(1)会报错，非捕获匹配

        //找到 韩顺平 这个关键字， 但是要求只是查找韩顺平教育和韩顺平老师中包含的韩顺平
        //String regStr = "韩顺平(?=教育|老师)"; //使用matcher.group(1)会报错，非捕获匹配
        //找到 韩顺平 这个关键字， 但是要求查找不是韩顺平教育和韩顺平老师中包含的韩顺平
        String regStr = "韩顺平(?!教育|老师)";
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }


    /**
     * 非贪婪匹配
     * */
    private void test11() {
        String content = "hello 11111ok";


        //String regStr = "\\d+"; //默认是贪婪匹配
        String regStr = "\\d+?"; //默认是贪婪匹配
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    /**
     * 匹配汉字
     * 邮政编码：1到9开头的六位数
     * QQ号：1到9开头的一个5到10位数
     * 手机号： 必须是13、14、15、18 开头的11位数
     * url:
     * */
    private void test12() {
        //汉字
        //String content = "打开";
        //String regStr = "^[\u0391-\uffe5]+$";

        //邮政编码
        //String content = "123007";
        //String regStr = "^[1-9]\\d{5}$";

        //QQ号
        //String content = "123007";
        //String regStr = "^[1-9]\\d{4,9}$";

        //手机号
        //String content = "18823185102";
        //String regStr = "^1[3|4|5|8]\\d{9}$";

        //url
        String content = "https://www.baidu.com";
        String regStr = "^((http|https)://)([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$"; //?./在中括号中代表字符本身意义

        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        if (matcher.find()) {
            Log.i(TAG, "》》》》》找到内容---满足条件");
        } else {
            Log.i(TAG, "》》》》》找到内容---不满足条件");
        }
    }

    /**
     * 演示Pattern.matches方法，用于整体匹配，在验证输入的字符串是否满足条件使用
     * */
    private void test13() {
        String content = "hello abc hello";
        String regStr = "hello"; //

        boolean matches = Pattern.matches(regStr, content);//false, 需要整体匹配，也就是 ^hello$ 是否符合条件。
        if (matches) {
            Log.i(TAG, "》》》》》找到内容---满足条件");
        } else {
            Log.i(TAG, "》》》》》找到内容---不满足条件");
        }
    }

    /**
     * Matcher类的方法
     * */
    private void test14() {
        String content = "hello123 aa hello123 456";

        String regStr = "(hello)(123)";
        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            Log.i(TAG, "》》》》》找到内容index = " + index + ", start = " + matcher.start() + ", end=" + matcher.end());
            Log.i(TAG, "》》》》》找到内容index = " + index + ", start0 = " + matcher.start(0) + ", end=" + matcher.end(0));
            Log.i(TAG, "》》》》》找到内容index = " + index + ", start1 = " + matcher.start(1) + ", end=" + matcher.end(1));
            Log.i(TAG, "》》》》》找到内容index = " + index + ", start2 = " + matcher.start(2) + ", end=" + matcher.end(2));
            index++;
        }

        //整体匹配相当于^(hello)(123)$
        boolean bAll = matcher.matches();
        Log.i(TAG, "》》》》》找到内容 整体匹配 = " + bAll); //false


        //hello替换成陈益洪， 返回的结果会是新的，旧的数据还是原来的数据。
        regStr = "hello";
        pattern = Pattern.compile(regStr); //创建模式对象
        matcher = pattern.matcher(content); //创建匹配器
        String newContent = matcher.replaceAll("陈益洪");
        Log.i(TAG, "》》》》》找到内容 老数据 = " + content);
        Log.i(TAG, "》》》》》找到内容 新数据 = " + newContent);
    }

    //分组、捕获、反向引用
    //给你一段文本，请你找出所有四个数字连在一起的子串，并且这四个数字要满足第1位与第4位相同，第2位与第3位相同，比如：1221,、5775
    //分组：小括号括起来的内容
    //捕获：可以用编号找的的小括号中的内容比如:(pattern)、(?<name>pattern)
    //反向引用：前面小括号内的捕获的内容，可以在后面引用。内部反向引用：\\分组号， 外部反向引用：$分组号
    // 反向引用的例子： 1： 要匹配两个连续的相同的数字: (\\d)\\1
    //                2:  要匹配五个连续的相同的数字：(\\d)\\1{4}
    //                3:  要匹配个位与千位相同，十位与百位相同的四位数 5225  ： (\\d)(\\d)\\2\\1
    private void test15() {
        //String content = "hello jack tom11 jack22 yyy xxx";
        //String regStr = "(\\d)\\1";

        //String content = "hello jack tom11222 jack22222 yyy xxx";
        //String regStr = "(\\d)\\1{4}";

        String content = "hello jack tom1221 jack22222 yyy xxx";
        String regStr = "(\\d)(\\d)\\2\\1";

        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        while (matcher.find()) {
            String str = matcher.group();
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }
    }

    //把类似： "我....我要....学学学学....编程java!" 修改成 "我要学编程java!"
    //测试 $1 外部反向引用去除重复字
    private void test16() {
        String content = "我....我要....学学学学....编程java!";
        //1.去掉所有的.
        String regStr = "\\.";

        Pattern pattern = Pattern.compile(regStr); //创建模式对象
        Matcher matcher = pattern.matcher(content); //创建匹配器
        content = matcher.replaceAll("");
        Log.i(TAG, "》》》》》找到内容 去掉点后：" + content);
        //2.去掉所有重复字
        //思路
        //(1) 使用(.)\\1+
        //(2) 使用 $1 来替换匹配到的内容
        regStr = "(.)\\1+";
        pattern = Pattern.compile(regStr); //创建模式对象
        matcher = pattern.matcher(content); //创建匹配器
        int index = 0; //打印的框架有问题，不加上这个如果内容相同，只会打印两个。
        /*while (matcher.find()) {
            String str = matcher.group();
            Log.i(TAG, "》》》》》找到内容index = " + index + ", str = " + str);
            index++;
        }*/
        content = matcher.replaceAll("$1"); //外部反向引用
        Log.i(TAG, "》》》》》找到内容 去掉重复字后：" + content);
    }

    /**
     * String类中使用正则表达式
     * */

    private void test17() {
        String content = "abc jdk1.3 bad jdk1.4 dad";
        //jdk1.3 和 jdk1.4 替换成jdk
        content = content.replaceAll("jdk1\\.3|jdk1\\.4", "jdk"); //旧String不会改变，赋值给新String才是改变的变量
        Log.i(TAG, "》》》》》找到内容 替换后的内容：" + content);

        //String.matches() 是否符合条件
        content = "13812341234";
        boolean isb = content.matches("1(38|39)\\d{8}");
        Log.i(TAG, "》》》》》找到内容 是否匹配：" + isb);

        //String.split() 分割字符
        content = "hello#abc-jack123smith~北京";
        String[] splits = content.split("#|-|~|\\d+");
        for(String s : splits) {
            Log.i(TAG, "》》》》》找到内容 分割后的内容：" + s);
        }
    }

    /**
     * 邮件规则：
     * 1.只能有一个@
     * 2.@前面是用户名，可以是a-z A-Z 0-9 _-字符
     * 3.@后面是域名，并且域名只能是英文字母，比如 sohu.com或者tsinghua.org.cn
     * */

    private void test18() {
        String content = "hsp@shu.com";
        String regStr = "[\\w-]+@([a-zA-Z]+\\.)+[a-zA-Z]+";
        boolean isb = content.matches(regStr); //matches这个函数相当于在最前面和最后面分别加上了^和$
        Log.i(TAG, "》》》》》找到内容 是否是邮件：" + isb);
    }

    /**
     * 要求验证是否是正整数或者小数
     * 比如：123、-456、34.23、-89.8、-0.001、0.14
     * */
    private void test19() {
        String content = "-0.001";
        String regStr = "^[-|+]?([1-9]\\d*|0)(\\.\\d+)?$";
        boolean isb = content.matches(regStr);
        Log.i(TAG, "》》》》》找到内容 是否是数字：" + isb);
    }

    /**
     * 要求验证url
     * */
    private void test20() {
        String content = "http://www.sohu.com:8080/abc/index.html";
        String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):(\\d+)[\\w-/]*/([\\w.]+)$";
        boolean isb = content.matches(regStr);
        Log.i(TAG, "》》》》》找到内容 是否是url：" + isb);
    }

}
